// Package mq provides RabbitMQ message queue functionality for the WePayKit application.
// It manages the connection to the RabbitMQ server and provides methods for sending and receiving messages.
package mq

import (
    "fmt"

    "github.com/streadway/amqp"
    "github.com/we-pay-kit/we-pay-kit/internal/config"
    "github.com/we-pay-kit/we-pay-kit/internal/logger"
)

// RabbitMQClient wraps the RabbitMQ connection and channel to provide message queue operations.
type RabbitMQClient struct {
	conn    *amqp.Connection
	channel *amqp.Channel
}

// rabbitMQInstance holds the singleton RabbitMQ client instance.
var rabbitMQInstance *RabbitMQClient

// GetRabbitMQ returns the singleton RabbitMQ client instance.
// It initializes the RabbitMQ connection on first call.
func GetRabbitMQ() *RabbitMQClient {
	if rabbitMQInstance == nil {
		rabbitMQInstance = initRabbitMQ()
	}
	return rabbitMQInstance
}

// initRabbitMQ initializes the RabbitMQ connection from config.Get().
func initRabbitMQ() *RabbitMQClient {
	logger.Info("Initializing RabbitMQ connection")
    
    appCfg := config.Get()
    mqCfg := appCfg.RabbitMQInfo

    logger.Info(fmt.Sprintf("RabbitMQ config: host=%s port=%d username=%s",
        mqCfg.Host, mqCfg.Port, mqCfg.Username))

	// Create RabbitMQ connection with configuration
    conn, err := amqp.Dial(fmt.Sprintf("amqp://%s:%s@%s:%d/",
        mqCfg.Username, mqCfg.Password, mqCfg.Host, mqCfg.Port))
	if err != nil {
		logger.Error(fmt.Sprintf("Failed to connect to RabbitMQ: %v", err))
		// We don't exit here as RabbitMQ might be optional in some environments
		return nil
	}

	ch, err := conn.Channel()
	if err != nil {
		logger.Error(fmt.Sprintf("Failed to open a channel: %v", err))
		// Close the connection if channel creation fails
		conn.Close()
		return nil
	}

	logger.Info("Successfully connected to RabbitMQ")
	return &RabbitMQClient{conn: conn, channel: ch}
}

// DeclareQueue declares a queue in RabbitMQ.
func (r *RabbitMQClient) DeclareQueue(name string) error {
	if r == nil || r.channel == nil {
		return nil // RabbitMQ not initialized
	}

	_, err := r.channel.QueueDeclare(
		name,  // name
		true,  // durable
		false, // delete when unused
		false, // exclusive
		false, // no-wait
		nil,   // arguments
	)
	return err
}

// Publish sends a message to a queue in RabbitMQ.
func (r *RabbitMQClient) Publish(queueName, message string) error {
	if r == nil || r.channel == nil {
		return nil // RabbitMQ not initialized
	}

	err := r.channel.Publish(
		"",        // exchange
		queueName, // routing key
		false,     // mandatory
		false,     // immediate
		amqp.Publishing{
			ContentType: "text/plain",
			Body:        []byte(message),
		})
	return err
}

// Consume creates a consumer for a queue in RabbitMQ.
func (r *RabbitMQClient) Consume(queueName string) (<-chan amqp.Delivery, error) {
	if r == nil || r.channel == nil {
		return nil, nil // RabbitMQ not initialized
	}

	return r.channel.Consume(
		queueName, // queue
		"",        // consumer
		true,      // auto-ack
		false,     // exclusive
		false,     // no-local
		false,     // no-wait
		nil,       // args
	)
}

// Close closes the RabbitMQ connection and channel.
func (r *RabbitMQClient) Close() error {
	if r == nil {
		return nil // RabbitMQ not initialized
	}

	var err error
	if r.channel != nil {
		err = r.channel.Close()
	}
	if r.conn != nil {
		connErr := r.conn.Close()
		if err == nil {
			err = connErr
		}
	}
	return err
}